﻿/*
 * TextServices / TSDotNet / TSNative
 * Marco Maria Bellocchi, Mauro Bampo
 * Copyright(c) 2013.
 * 
 * The MIT License
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 * 
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TSDotNet.Common;

namespace TSDotNet.Encoders
{
    public class TrailingZeroZipper : IEncoder
    {
        private char _separator;
        private char _compressedChar;
        private StateMachine _sm;

        //1,0,0,0=>1Z3
        public TrailingZeroZipper(char separator, char compressedChar)
        {
            _separator = separator;
            _compressedChar = compressedChar;
            _sm = new StateMachine(_separator, _compressedChar);
        }

        public string Name
        {
            get { throw new NotImplementedException(); }
        }

        public string Encode(string data)
        {
            char[] chars = data.ToCharArray();
            _sm.Data = chars;
            return _sm.Parse();
        }

        public string Decode(string data)
        {
            char[] chars = data.ToCharArray();
            int charsLen = chars.Length;
            StringBuilder sb = new StringBuilder(charsLen);
            for(int i=0; i < charsLen; i++)
            {
                char c = chars[i];
                if (c == _compressedChar)
                {
                    StringBuilder zeros = new StringBuilder();
                    if (i > 0)
                        sb.Append(_separator);
                    i++;
                    while (i < charsLen && chars[i] != _separator && chars[i] != '-')
                    {
                        zeros.Append(chars[i++]);
                    }
                    int numOfZero = int.Parse(zeros.ToString());
                    
                    for (int t = 0; t < numOfZero; t++)
                    {
                        sb.Append("0").Append(_separator);
                    }
                    if (i == charsLen)
                        sb.Length--;
                    else if (chars[i] == '-')
                        sb.Append('-');
                }
                else if (c == '-' && i!=0)
                {
                    sb.Append(_separator).Append(c);
                }
                else
                    sb.Append(c);
            }
            return sb.ToString();
        }

        protected class StateMachine
        {
            private const char _minus = '-';
            private const char _plus = '+';
            private const char _zero = '0';
            private const char _point = '.';
            private char[] _data;
            private char _separator;
            private char _compressedChar;
            private int _current;
            private char? _currentChar;
            //private int _firstZeroIndex = -1;
            private int _numOfZeros = 0;
            private int _dataLen;

            public StateMachine(char separator = ',', char compressedChar = 'z')
            {
                _separator = separator;
                _compressedChar = compressedChar;
                _current = -1;
            }

            public char[] Data
            {
                get
                {
                    return _data;
                }
                set
                {
                    _data = value;
                    _current = -1;
                    _currentChar = null;
                    _numOfZeros = 0;
                    _dataLen = _data.Length;
                }
            }

            private bool NextChar()
            {
                if (_current < _dataLen - 1)
                {
                    _current++;
                    _currentChar = _data[_current];
                    return true;
                }
                _currentChar = null;
                return false;
            }

            private bool PreviousChar()
            {
                if (_current > 0)
                {
                    _current--;
                    _currentChar = _data[_current];
                    return true;
                }
                _current = -1;
                _currentChar = null;
                return false;
            }

            private void WriteZeros(StringBuilder sb)
            {
                int zeros = 0;
                bool isFirstChar = _current == 0;
                while (_currentChar == _zero)
                {
                    zeros++;
                    NextChar();
                    NextChar();//read ,
                }
                if(_currentChar != null)
                    PreviousChar();
                if (zeros > 1)
                    sb.Append(_compressedChar).Append(zeros);
                else if (isFirstChar)
                    sb.Append(_zero);
                else
                    sb.Append(_separator).Append(_zero);
            }

            //_currentChar is the first digit
            private void WriteNumber(StringBuilder sb)
            {  
                sb.Append(_currentChar);
                while (NextChar() && _currentChar != _separator)
                {
                    sb.Append(_currentChar.Value);
                }                
            }

            private void FirstParse(StringBuilder sb)
            {
                if (_currentChar == _zero)
                {
                    if (NextChar() && _currentChar == _point)
                    {
                        WriteNumber(sb);
                    }
                    else
                    {
                        PreviousChar();
                        WriteZeros(sb);
                    }
                }
                else
                {
                    if (_currentChar == _plus)
                    {
                        NextChar();
                    }
                    else if (_currentChar == _minus)
                    {
                        sb.Append(_minus);
                        NextChar();
                    }
                    WriteNumber(sb);
                }
            }

            public string Parse()
            {
                StringBuilder sb = new StringBuilder(_dataLen);
                NextChar();
                FirstParse(sb);
                while (NextChar())
                {
                    if (_currentChar == _zero)
                    {
                        if (NextChar() && _currentChar == _point)
                        {
                            sb.Append(_separator);
                            WriteNumber(sb);
                        }
                        else
                        {
                            PreviousChar();
                            WriteZeros(sb);
                        }
                    }
                    else
                    {
                        if (_currentChar == _plus)
                        {
                            sb.Append(_separator);
                            NextChar();
                        }
                        else if (_currentChar == _minus)
                        {
                            sb.Append(_minus);
                            NextChar();
                        }
                        else
                        {                     
                             sb.Append(_separator);
                        }
                        WriteNumber(sb); 
                    }
                }
                return sb.ToString();
            }
            /*
             
              private void S (StringBuilder sb)
            {
                    if(_currentChar == _separator)
                    {
                        NextChar();
                        //if (_currentChar == null)
                        //    throw new ApplicationException("Cannot have null chat after a comma");
                        if (_currentChar == '0')
                        {
                            _numOfZeros++;
                            NextChar();
                            S(sb);
                            return;
                        }   
                        else if(_numOfZeros > 0)
                            sb.Append(_compressedChar).Append(_numOfZeros).Append(_separator).Append(_currentChar);
                        else
                            sb.Append(_separator).Append(_currentChar.Value);
                    }
                    else
                    {
                        if (_numOfZeros > 0)
                            sb.Append(_compressedChar).Append(_numOfZeros.ToString());
                        if (_currentChar.HasValue)
                            sb.Append(_currentChar.Value);
                        else
                            sb.Append(string.Empty);
                    }
                
            }*/
        }
    }
}



namespace TSDotNet.Encoders

{
    public class TrailingZeroZipper2 : IEncoder
    {
        private char _separator;
        private char _compressedChar;
        private StateMachine _sm;

        //1,0,0,0=>1Z3
        public TrailingZeroZipper2(char separator, char compressedChar)
        {
            _separator = separator;
            _compressedChar = compressedChar;
            _sm = new StateMachine(_separator, _compressedChar);
        }

        public string Name
        {
            get { throw new NotImplementedException(); }
        }

        public string Encode(string data)
        {
            char[] chars = data.ToCharArray();
            _sm.Data = chars;
            return _sm.Parse();
        }

        public string Decode(string data)
        {
            char[] chars = data.ToCharArray();
            int charsLen = chars.Length;
            StringBuilder sb = new StringBuilder(charsLen);
            for(int i=0; i < charsLen; i++)
            {
                char c = chars[i];
                if (c == _compressedChar)
                {
                    StringBuilder zeros = new StringBuilder();
                    i++;
                    while (i < charsLen && chars[i] != _separator)
                    {
                        zeros.Append(chars[i++]);
                    }
                    int numOfZero = int.Parse(zeros.ToString());
                    for (int t = 0; t < numOfZero; t++)
                    {
                        sb.Append(_separator).Append("0");
                    }
                    if (i < charsLen)
                        sb.Append(_separator);
                }
                else
                    sb.Append(c);
            }
            return sb.ToString();
        }

        protected class StateMachine
        {
            private char[] _data;
            private char _separator;
            private char _compressedChar;
            private int _current;
            private char? _currentChar;
            //private int _firstZeroIndex = -1;
            private int _numOfZeros = 0;
            private int _dataLen;

            public StateMachine(char separator = ',', char compressedChar = 'z')
            {
                _separator = separator;
                _compressedChar = compressedChar;
                _current = 0;
            }

            public char[] Data
            {
                get
                {
                    return _data;
                }
                set
                {
                    _data = value;
                    _current = 0;
                    _currentChar = null;
                    _numOfZeros = 0;
                    _dataLen = _data.Length;
                }
            }

            private bool NextChar()
            {
                if (_current < _dataLen)
                {
                    _currentChar = _data[_current++];
                    return true;
                }
                _currentChar = null;
                return false;
            }

            public string Parse()
            {
                StringBuilder sb = new StringBuilder(_dataLen);
                while (NextChar())
                {
                    //S(sb);
                    //_numOfZeros = 0;
                    if (_currentChar != _separator)
                    {
                        if (_numOfZeros > 0)
                        {
                            sb.Append(_compressedChar).Append(_numOfZeros);
                            _numOfZeros = 0;
                        }
                        if (_currentChar.HasValue)
                            sb.Append(_currentChar.Value);
                    }
                    else
                    {
                        NextChar();
                        if (_currentChar != '0')
                        {
                            if (_numOfZeros > 0)
                            {
                                sb.Append(_compressedChar).Append(_numOfZeros).Append(_separator).Append(_currentChar);
                                _numOfZeros = 0;
                            }
                            else
                                sb.Append(_separator).Append(_currentChar.Value);
                        }
                        else
                        {
                            _numOfZeros++;
                        }
                    }
                }
                if (_numOfZeros > 0)
                {
                    sb.Append(_compressedChar).Append(_numOfZeros.ToString());
                    _numOfZeros = 0;
                }
                return sb.ToString();
            }

            private void S (StringBuilder sb)
            {
                    if (_currentChar != _separator)
                    {
                        if (_numOfZeros > 0)
                        {
                            sb.Append(_compressedChar).Append(_numOfZeros.ToString());
                            _numOfZeros = 0;
                        }
                        if (_currentChar.HasValue)
                            sb.Append(_currentChar.Value);
                        else
                            sb.Append(string.Empty);
                    }
                    else
                    {
                        NextChar();
                        //if (_currentChar == null)
                        //    throw new ApplicationException("Cannot have null chat after a comma");
                        if (_currentChar != '0')
                        {
                            if (_numOfZeros > 0)
                            {
                                sb.Append(_compressedChar).Append(_numOfZeros).Append(_separator).Append(_currentChar);
                                _numOfZeros = 0;
                            }
                            else
                                sb.Append(_separator).Append(_currentChar.Value);
                        }
                        else
                        {
                            _numOfZeros++;
                            NextChar();
                            S(sb);
                        }
                    }
            }
        }
    }
}
            /*
             
              private void S (StringBuilder sb)
            {
                    if(_currentChar == _separator)
                    {
                        NextChar();
                        //if (_currentChar == null)
                        //    throw new ApplicationException("Cannot have null chat after a comma");
                        if (_currentChar == '0')
                        {
                            _numOfZeros++;
                            NextChar();
                            S(sb);
                            return;
                        }   
                        else if(_numOfZeros > 0)
                            sb.Append(_compressedChar).Append(_numOfZeros).Append(_separator).Append(_currentChar);
                        else
                            sb.Append(_separator).Append(_currentChar.Value);
                    }
                    else
                    {
                        if (_numOfZeros > 0)
                            sb.Append(_compressedChar).Append(_numOfZeros.ToString());
                        if (_currentChar.HasValue)
                            sb.Append(_currentChar.Value);
                        else
                            sb.Append(string.Empty);
                    }
                
            }
             
        }
    }
}*/

 
